package CiscoTalkOO;

use strict;
use warnings;

use Expect;
use POSIX qw(strftime);
use Net::Telnet;
use Getopt::Long;
use Proc::ProcessTable;
use Data::Dumper;

sub new {
	my $self = {};
	my $object = shift;
	my $ip = shift;
	my $login = shift;
	my $pwd1 = shift;
	my $pwd2 = shift;
	$self->{rExp} = undef;
	$self->{IP} = $ip;
	$self->{spawn_ok} = 0;
	$self->{current_connection_method} = -1;
	$self->{connection_error} = 0;
	$self->{login} = $login;
	$self->{pwd1} = $pwd1;
	$self->{pwd2} = $pwd2;
	$self->{prefered_connection_methon} = 0;
	$self->{error_commands} = ();
	$self->{error_lines} = ();
	$self->{commands_had_errors} = 0;

	bless ($self);
	return $self;
}

sub TelnetPing
{
	my $self = shift;
	my $result;
	my $count;
	my $repeat;
	my $t;

    while ( $result == 0 && $count < $repeat ) {
        if ( ! $t -> open( "{$self->{ip}}" ) ) {
            $t -> close;
            $result=0;
        } else {
            $t -> close;
            $result=1;
        }
        $count++;
    }

    if ( $result == 0 ) {
        return 0;
    } else {
        return 1;
    }
}


sub StartTalking
{
	my $self = shift;
	my $device = shift;				#IP
	my $login = shift;
	my $telnet_pwd = shift;
	my $enable_pwd = shift;
	my $temp;

	$self->{rExp} = new Expect;
	$self->{rExp}->log_stdout(1);

	my $connection_type="telnet";

#	if ( TelnetPingTest( $device, 1, 1 ) == 1 ) {
#		$self->connectTelnet();
#	}

	$self->connectTelnet();
	
	if ( $self->{spawn_ok} == 0 ) {
		$self->{rExp} = undef;
		$self->{rExp} = new Expect;
		$self->connectSSHv1();
	} else {
		$self->{current_connection_method} = "TELNET";
	}

	if ( $self->{spawn_ok} == 0 ) {
		$self->{rExp} = undef;
		$self->{rExp} = new Expect;
		$self->connectSSHv2();
		if ( $self->{spawn_ok} == 1 ) {
			$self->{current_connection_method} = "SSHv2";
		}
	} else {
		$self->{current_connection_method} = "SSHv1";
	}



}

sub connectTelnet
{
	my $self = shift;

	$self->{rExp}->spawn("telnet " . $self->{IP} . " 2>&1");

	#my $user_prompt = '[^ \r\n]*>';
	my $user_prompt = '>';
	my $enable_prompt = '[^ \r\n]*\#';
	my $config_global_prompt = '[^ \r\n]*.config.\#';
	my $config_line_prompt = '[^ \r\n]*.config-l\[ine]{0,3}.\#';
	my $config_interface_prompt = '[^ \r\n]*.config-(if?|s.*).\#';	
	my $config_acl_prompt = '[^ \r\n]*.config-\[^ \r\n]*\#';
	my $catos_enable_prompt = '[^ \r\n]*> \(enable\)';

	my $spawn_ok=0;
	my $connection_error=0; # 1 = auth fail, 2 = login loop
	my $login_count = 1;

	$self->{rExp} -> expect( 15, 
	                             [ qr/(?i)authentication failed/, 
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 1;
								   }
								 ],

								 [ qr/(?i)login failed/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 1;
								   }
								 ],

								 [ qr/(?i)connection timed out/,
								   sub {
									   $spawn_ok = 0;
									   $connection_error = 0;
								   }
								 ],
								 
								 [ qr/(?i)username:/,
								   sub {
									   $spawn_ok = 1;
									   my $fh = shift;
									   $fh -> send($self->{login} . "\n");
									   $login_count++;
									   if ( $login_count > 5 ) { 
										   $connection_error = 2;
									   } else {
										   exp_continue;
									   }
								   }
								 ],

								 [ qr/(?i)password:/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send($self->{pwd1} . "\n");
									   exp_continue;
								   }
								 ],

								 [ qr/$user_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("ena\n");
								   }
								 ],

								 [ qr/$enable_prompt|$catos_enable_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("\n");
								   }
								 ],
								 
								 [ qr/(?i)Connection refused/,
								   sub {
									   $spawn_ok = 0;
								   }
								 ] 
						  );
	
	if ( $connection_error == 0 && $spawn_ok == 1 ) {
		$self->{rExp} -> expect( 15, 
		                             [ qr/(?i)password:/,
									   sub { 
									  	   $spawn_ok = 1;
										   my $fh = shift;
										   $fh -> send($self->{pwd2}."\n");
									   }
									 ],

									 [ qr/(?i)error in authentication/,
									   sub { 
										   $spawn_ok = 1;
										   $connection_error = 1;
									   }
									 ],

									 [ qr/(?i)invalid input detected/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 3;
									   }
									 ],

									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub { 
										   $spawn_ok = 1;
										   $connection_error = 0;
										   my $fh = shift;
										   $fh -> send("\n");
									   }
									 ]
								);
	} 

	if ( $connection_error == 0 && $spawn_ok == 1 ) {
		$self->{rExp} -> expect( 15,
		                             [ qr/(?i)error in authentication/,
									   sub { 
										   $spawn_ok = 1;
										   $connection_error = 1;
									   }
									 ],

									 [ qr/(?i)invalid input detected/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 3;
									   }
									 ],

									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 0;
									   }
									 ],
							    );
	}

	$self->{connection_error} = $connection_error;
	$self->{spawn_ok} = $spawn_ok;

	print "spawn_ok = " . $spawn_ok . "\n";

	if ( $spawn_ok == 0 || $connection_error != 0 ) {
		$self->{rExp}->close();
	}

}

sub connectSSHv1
{
	my $self = shift;

	my $spawn_ok = 0;
	my $connection_error = 0; # 1 = auth fail, 2 = login loop

	$self->{rExp}->spawn("ssh -1 -l " . $self->{login} . " " . $self->{IP} . " 2>&1");

	my $user_prompt = '[^ \r\n]*>';
	my $enable_prompt = '[^ \r\n]*\#';
	my $config_global_prompt = '[^ \r\n]*.config.\#';
	my $config_line_prompt = '[^ \r\n]*.config-l\[ine]{0,3}.\#';
	my $config_interface_prompt = '[^ \r\n]*.config-(if?|s.*).\#';	
	my $config_acl_prompt = '[^ \r\n]*.config-\[^ \r\n]*\#';
	my $catos_enable_prompt = '[^ \r\n]*> \(enable\)';


	$self->{rExp} -> expect( 15, [ timeout => sub { $spawn_ok = 0; $connection_error = 0; } ],
								 [ qr/\(yes\/no\)/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("yes\n");
									   exp_continue;
								   }
								 ],

								 [ qr/(?i)invalid input detected/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 3;
								   }
								 ],

								 [ qr/(?i)connection timed out/,
								   sub {
									   $spawn_ok = 0;
									   $connection_error = 0;
								   }
								 ],

	                             [ qr/(?i)Protocol major versions differ: 1 vs. 2/,
								   sub {
									   $spawn_ok = 0;
								   }
								 ],

	                             [ qr/(?i)password:/,
								   sub {
									   $spawn_ok = 1;
									   my $fh = shift;
									   $fh -> send($self->{pwd1} . "\n");
									   exp_continue;
								   }
                                 ],
								 
								 [ qr/$user_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("ena\n");
								   }
								 ],

								 [ qr/$enable_prompt|$catos_enable_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("\n");
								   }
								 ],

								 [ qr/(?i)Permission denied/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 1;
								   }
								 ]
						   );

	if ( $spawn_ok == 1 && $connection_error == 0 ) {
		$self->{rExp} -> expect( 15, [ timeout => sub { $spawn_ok = 1; $connection_error = 1; } ],
		                             [ qr/(?i)password:/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 0;
										   my $fh = shift;
										   $fh -> send($self->{pwd2} . "\n");
										   exp_continue;
									   }
									 ],
									 
									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 0;
										   my $fh = shift;
										   $fh -> send("\n");
									   }
									 ]
							   );
	}

	if ( $spawn_ok == 1 && $connection_error == 0 ) {
		$self->{rExp} -> expect( 15,
		                             [ timeout => sub { $spawn_ok = 1; $connection_error = 1; } ],
									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub {
									  	   $spawn_ok = 1;
										   $connection_error = 0;
									   }
									 ]
							   );
	}



	$self->{spawn_ok} = $spawn_ok;
	$self->{connection_error} = $connection_error;
	if ( $spawn_ok == 0 || $connection_error != 0 ) {
		$self->{rExp}->close();
	}
	print "spawn_ok = " . $spawn_ok . "\n";
}

sub connectSSHv2
{
	my $self = shift;

	my $spawn_ok = 0;
	my $connection_error = 0; # 1 = auth fail, 2 = login loop

	$self->{rExp}->spawn("ssh -l " . $self->{login} . " " . $self->{IP} . " 2>&1");

	my $user_prompt = '[^ \r\n]*>';
	my $enable_prompt = '[^ \r\n]*\#';
	my $config_global_prompt = '[^ \r\n]*.config.\#';
	my $config_line_prompt = '[^ \r\n]*.config-l\[ine]{0,3}.\#';
	my $config_interface_prompt = '[^ \r\n]*.config-(if?|s.*).\#';	
	my $config_acl_prompt = '[^ \r\n]*.config-\[^ \r\n]*\#';
	my $catos_enable_prompt = '[^ \r\n]*> \(enable\)';


	$self->{rExp} -> expect( 15, [ timeout => sub { $spawn_ok = 0; $connection_error = 0; } ],
								 [ qr/\(yes\/no\)/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("yes\n");
									   exp_continue;
								   }
								 ],

								 [ qr/(?i)invalid input detected/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 3;
								   }
								 ],

								 [ qr/(?i)connection timed out/,
								   sub {
									   $spawn_ok = 0;
									   $connection_error = 0;
								   }
								 ],

	                             [ qr/(?i)Protocol major versions differ: 1 vs. 2/,
								   sub {
									   $spawn_ok = 0;
								   }
								 ],

	                             [ qr/(?i)password:/,
								   sub {
									   $spawn_ok = 1;
									   my $fh = shift;
									   $fh -> send($self->{pwd1} . "\n");
									   exp_continue;
								   }
                                 ],
								 
								 [ qr/$user_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("ena\n");
								   }
								 ],

								 [ qr/$enable_prompt|$catos_enable_prompt/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 0;
									   my $fh = shift;
									   $fh -> send("\n");
								   }
								 ],

								 [ qr/(?i)Permission denied/,
								   sub {
									   $spawn_ok = 1;
									   $connection_error = 1;
								   }
								 ]
						   );

	if ( $spawn_ok == 1 && $connection_error == 0 ) {
		$self->{rExp} -> expect( 15, [ timeout => sub { $spawn_ok = 1; $connection_error = 1; } ],
		                             [ qr/(?i)password:/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 0;
										   my $fh = shift;
										   $fh -> send($self->{pwd2} . "\n");
										   exp_continue;
									   }
									 ],
									 
									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub {
										   $spawn_ok = 1;
										   $connection_error = 0;
										   my $fh = shift;
										   $fh -> send("\n");
									   }
									 ]
							   );
	}

	if ( $spawn_ok == 1 && $connection_error == 0 ) {
		$self->{rExp} -> expect( 15,
		                             [ timeout => sub { $spawn_ok = 1; $connection_error = 1; } ],
									 [ qr/$enable_prompt|$catos_enable_prompt/,
									   sub {
									  	   $spawn_ok = 1;
										   $connection_error = 0;
									   }
									 ]
							   );
	}



	$self->{spawn_ok} = $spawn_ok;
	$self->{connection_error} = $connection_error;
	if ( $spawn_ok == 0 || $connection_error != 0 ) {
		$self->{rExp}->close();
	}
	print "spawn_ok = " . $spawn_ok . "\n";
}


sub DoExecute
{
#	my $command = "@_[0]";
#	my $logFile = "@_[1]";
	my $self = shift;
 	my $commands = shift;

	my $user_prompt = '[^ \r\n]*>';
	my $enable_prompt = '[^ \r\n]*\#';
	my $config_global_prompt = '[^ \r\n]*.config.\#';
	my $config_line_prompt = '[^ \r\n]*.config-l\[ine]{0,3}.\#';
	my $config_interface_prompt = '[^ \r\n]*.config-(if?|s.*).\#';
	my $config_acl_prompt = '[^ \r\n]*.config-\[^ \r\n]*\#';
	my $catos_enable_prompt = '[^ \r\n]*> \(enable\)';

	$self->{rExp} -> restart_timeout_upon_receive(1);
#	$self->{rExp} -> log_file($logFile);
	$self->{rExp} -> log_stdout(1);

	$self->{rExp} -> send("\n");
	my $previous_command = "";
	$self->{commands_had_errors} = 0;
 	foreach my $command ( @{$commands} ) {
		if ( $command =~ /^\s*$/ ) { $command = ""; }
		$self->{rExp} -> expect( 90,

				[ qr/\%([^\n\r]*)/,
				  sub {
					  my $fh = shift;
					  $self->{commands_had_errors} = 1;
					  push @{$self->{error_commands}}, $previous_command;
					  push @{$self->{error_lines}}, $fh->matchlist()->[0];
					  $fh -> send("\n");
					  exp_continue;
				  }
				],
				[ qr/\?/,
				  sub {
					  my $fh = shift;
					  $fh -> send($command . "\n");
				  } ], 
				[ qr/$user_prompt|$enable_prompt|$config_global_prompt|$config_line_prompt|$config_interface_prompt|$config_acl_prompt|$catos_enable_prompt/,
				  sub {
				    my $fh = shift;
				    $fh -> send($command . "\n");
				  } ],
				[ 'timeout',
				  sub {
					print "TIMEOUT\n\n"
				  }
				]
				);
        if ( $command ne "" ) { 
			$previous_command = $command;
		}
 	}
}

sub DoExit
{
	my $self = shift;

	my $user_prompt = '[^ \r\n]*>';
	my $enable_prompt = '[^ \r\n]*\#';
	my $config_global_prompt = '[^ \r\n]*.config.\#';
	my $config_line_prompt = '[^ \r\n]*.config-l\[ine]{0,3}.\#';
	my $config_interface_prompt = '[^ \r\n]*.config-(if?|s.*).\#';
	my $config_acl_prompt = '[^ \r\n]*.config-\[^ \r\n]*\#';
	my $catos_enable_prompt = '[^ \r\n]*> \(enable\)';

	$self->{rExp} -> send ("\n");
	$self->{rExp} -> expect( 5, '-re', "$user_prompt|$enable_prompt|$config_global_prompt|$config_line_prompt|$config_interface_prompt|$config_acl_prompt|$catos_enable_prompt" );
	$self->{rExp} -> send ("exit\n");

	return 0;
}

sub StartInteract
{
	my $self = shift;
	
	$self->{rExp} -> interact();
}

sub GoodBye
{
	my $self = shift;
	$self->{rExp} -> soft_close();
	return 0;
}

sub DESTROY {
	my $self = shift;
	$self->{rExp} = undef;
}
1;
